package com.ruoyi.common.utils.ps2;

import javax.imageio.*;
import javax.imageio.stream.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ImageDownloaderWithReportOnlyByXlsx {

    // 配置参数
    private static final long MAX_FILE_SIZE = 8 * 1024 * 1024; // 8MB
    private static final int MIN_WIDTH = 2048;
    private static final int MIN_HEIGHT = 2048;
    private static final int BATCH_SIZE = 10000;
    private static final String BASE_DIR = "G:\\stock_photos";
    private static final String XLSX_FILE = "C:\\Users\\xinhuiRong\\Desktop\\新建文件夹\\20万原图 20250811.xlsx";
    private static final String REPORT_FILE = "download_report.csv";
    private static final int MAX_RETRIES = 2;
    private static final int RETRY_DELAY = 2000;
    private static final String ERROR_LOG_FILE = "error_log.txt";

    // 内存限制配置
    private static final int MAX_MEMORY_USAGE_MB = 4096; // 最大内存使用4GB
    private static final int MEMORY_PER_TASK_MB = 200;  // 每个任务预估内存

    // 线程安全计数器
    private static final AtomicInteger fileCounter = new AtomicInteger(0);
    private static final Semaphore memorySemaphore = new Semaphore(
            MAX_MEMORY_USAGE_MB / MEMORY_PER_TASK_MB
    );

    // 优化的线程池配置
    private static final ExecutorService downloadExecutor = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    public static void main(String[] args) {
        try {
            // 初始化目录和文件
            Path basePath = Paths.get(BASE_DIR);
            createDirIfNotExists(basePath);

            Path reportPath = Paths.get(BASE_DIR, REPORT_FILE);
            Path errorLogPath = Paths.get(BASE_DIR, ERROR_LOG_FILE);
            initReportFile(reportPath);
            initErrorLogFile(errorLogPath);

            // 加载最后文件编号
            fileCounter.set(loadLastFileNumber(reportPath));
            System.out.println("起始文件编号: " + fileCounter.get());

            // 加载数据
            Set<String> processedStockNos = loadProcessedStockNos(reportPath);
            List<ImageRecord> records = loadXlsxData(Paths.get(XLSX_FILE));
            List<ImageRecord> pendingRecords = filterPendingRecords(records, processedStockNos);

            // 检查记录数量限制
            if (pendingRecords.size() > (999999 - fileCounter.get())) {
                System.err.printf("警告: 待处理图片数量(%d)将超过最大限制(%d)%n",
                        pendingRecords.size(), 999999 - fileCounter.get());
                pendingRecords = pendingRecords.subList(0, 999999 - fileCounter.get());
            }

            // 并行下载处理
            parallelDownloadAndProcess(pendingRecords, reportPath, errorLogPath);

            System.out.println("处理完成!");
        } catch (Exception e) {
            System.err.println("程序执行出错: " + e.getMessage());
            e.printStackTrace();
        } finally {
            downloadExecutor.shutdown();
        }
    }

    /**
     * 内存优化的并行处理方法
     */
    private static void parallelDownloadAndProcess(List<ImageRecord> records,
                                                   Path reportPath, Path errorLogPath) {
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failureCount = new AtomicInteger(0);

        List<Future<?>> futures = new ArrayList<>();

        for (ImageRecord record : records) {
            try {
                memorySemaphore.acquire();
                futures.add(downloadExecutor.submit(() -> {
                    try {
                        processSingleImage(record, reportPath, errorLogPath, successCount);
                    } catch (Exception e) {
                        failureCount.incrementAndGet();
                        writeErrorLog(errorLogPath, record.downloadUrl, e.getMessage());
                    } finally {
                        memorySemaphore.release();
                        logMemoryStats(successCount.get());
                    }
                }));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        // 等待所有任务完成
        awaitCompletion(futures);
        System.out.printf("下载完成! 成功: %d 张, 失败: %d 张%n",
                successCount.get(), failureCount.get());
    }

    /**
     * 处理单个图片（内存优化版）
     */
    private static void processSingleImage(ImageRecord record, Path reportPath,
                                           Path errorLogPath, AtomicInteger successCount) throws IOException, InterruptedException {
        String status = "failure";
        String errorMessage = "";
        String originalFilename = extractFilenameFromUrl(record.downloadUrl);
        String newFilename = "";

        // 获取唯一文件编号
        int currentCount = fileCounter.incrementAndGet();
        int currentBatch = (currentCount / BATCH_SIZE) + 1;

        // 创建批次目录
        Path batchDir = Paths.get(BASE_DIR, "batch_" + currentBatch);
        synchronized (ImageDownloaderWithReportOnlyByXlsx.class) {
            createDirIfNotExists(batchDir);
        }

        // 生成5位数字编号的文件名
        String fileName = String.format("原图4_%07d.jpg", currentCount);
        Path outputPath = batchDir.resolve(fileName);

        // 带重试机制的下载和压缩
        boolean success = false;
        for (int i = 0; i < MAX_RETRIES && !success; i++) {
            try {
                if (downloadAndSmartCompress(record.downloadUrl, outputPath)) {
                    success = true;
                    successCount.incrementAndGet();
                    status = "success";
                    newFilename = "batch_" + currentBatch + "/" + fileName;

                    // 每10张打印进度
                    if (successCount.get() % 10 == 0) {
                        System.out.printf("已下载: %d 张, 当前批次: %d, 文件名: %s%n",
                                successCount.get(), currentBatch, fileName);
                    }
                }
            } catch (Exception e) {
                errorMessage = e.getMessage();
                if (i < MAX_RETRIES - 1) Thread.sleep(RETRY_DELAY);
            }
        }

        // 记录结果
        writeReport(reportPath, record.stockNo, originalFilename,
                newFilename, status, errorMessage);
    }

    /**
     * 内存优化的下载和压缩方法
     */
    private static boolean downloadAndSmartCompress(String imageUrl, Path outputPath) throws IOException {
        Path tempFile = Files.createTempFile("download-", ".tmp");
        try {
            // 1. 下载到临时文件
            if (!downloadToFile(imageUrl, tempFile)) {
                return false;
            }

            // 2. 检查文件大小
            long fileSize = Files.size(tempFile);
            if (fileSize <= MAX_FILE_SIZE) {
                Files.move(tempFile, outputPath, StandardCopyOption.REPLACE_EXISTING);
                return true;
            }

            // 3. 使用内存优化的压缩
            return compressWithMemoryLimit(tempFile, outputPath);
        } finally {
            Files.deleteIfExists(tempFile);
        }
    }

    /**
     * 带内存限制的压缩方法
     */
    private static boolean compressWithMemoryLimit(Path source, Path target) throws IOException {
        try (ImageInputStream iis = ImageIO.createImageInputStream(source.toFile())) {
            Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
            if (!readers.hasNext()) return false;

            ImageReader reader = readers.next();
            try {
                reader.setInput(iis);

                // 分块读取参数设置
                ImageReadParam readParam = reader.getDefaultReadParam();
                int width = reader.getWidth(0);
                int height = reader.getHeight(0);

                // 计算采样率
                int subsample = calculateSubsampling(width, height);
                readParam.setSourceSubsampling(subsample, subsample, 0, 0);

                // 读取图像
                BufferedImage image = reader.read(0, readParam);

                // 使用流式压缩
                return streamCompress(image, target);
            } finally {
                reader.dispose();
            }
        }
    }

    /**
     * 流式压缩方法
     */
    private static boolean streamCompress(BufferedImage image, Path target) {
        try (OutputStream os = Files.newOutputStream(target);
             ImageOutputStream ios = ImageIO.createImageOutputStream(os)) {

            ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
            try {
                writer.setOutput(ios);

                ImageWriteParam writeParam = writer.getDefaultWriteParam();
                writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                writeParam.setCompressionQuality(0.7f);

                writer.write(null, new IIOImage(image, null, null), writeParam);
                return true;
            } finally {
                writer.dispose();
            }
        } catch (IOException e) {
            System.err.println("压缩失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 计算图像采样率
     */
    private static int calculateSubsampling(int width, int height) {
        long estimatedSize = (long) width * height * 3; // 3 bytes per pixel (RGB)
        int subsample = 1;

        // 根据预估内存需求调整采样率
        while (estimatedSize / (subsample * subsample) > MEMORY_PER_TASK_MB * 1024L * 1024L) {
            subsample++;
        }

        return Math.min(subsample, 4); // 最大采样4x4
    }

    /**
     * 内存状态监控
     */
    private static void logMemoryStats(int processedCount) {
        if (processedCount % 100 == 0) {
            Runtime rt = Runtime.getRuntime();
            long usedMB = (rt.totalMemory() - rt.freeMemory()) / (1024 * 1024);
            long maxMB = rt.maxMemory() / (1024 * 1024);
            System.out.printf("Memory: %d/%d MB (%.1f%%) | Tasks: %d/%d%n",
                    usedMB, maxMB, (usedMB * 100.0 / maxMB),
                    (memorySemaphore.availablePermits()),
                    (MAX_MEMORY_USAGE_MB / MEMORY_PER_TASK_MB));

            if (usedMB > maxMB * 0.7) {
                System.gc(); // 内存使用超过70%时触发GC
            }
        }
    }

    /**
     * 下载图片到本地文件
     */
    private static boolean downloadToFile(String imageUrl, Path targetFile) {
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10000); // 10秒连接超时
            conn.setReadTimeout(60000);    // 60秒读取超时

            // 检查HTTP响应码
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                System.err.printf("下载失败 [%s]: HTTP错误 %d%n", imageUrl, conn.getResponseCode());
                return false;
            }

            // 下载图片到文件
            try (InputStream in = conn.getInputStream()) {
                Files.copy(in, targetFile, StandardCopyOption.REPLACE_EXISTING);
            }
            return true;
        } catch (Exception e) {
            System.err.printf("下载失败 [%s]: %s%n", imageUrl, e.getMessage());
            return false;
        }
    }

    /**
     * 等待所有任务完成
     */
    private static void awaitCompletion(List<Future<?>> futures) {
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (Exception e) {
                System.err.println("任务执行出错: " + e.getMessage());
            }
        }
    }

    /**
     * 写入错误日志（同步方法）
     */
    private synchronized static void writeErrorLog(Path errorLogPath, String url, String error) {
        try (BufferedWriter writer = Files.newBufferedWriter(errorLogPath,
                StandardCharsets.UTF_8, StandardOpenOption.APPEND)) {
            writer.write(String.format("[%s] %s | 错误: %s%n", new Date(), url, error));
        } catch (IOException e) {
            System.err.println("写入错误日志失败: " + e.getMessage());
        }
    }

    /**
     * 写入报告（同步方法）
     */
    private synchronized static void writeReport(Path reportPath, String stockNo,
                                                 String originalFilename, String newFilename,
                                                 String status, String errorMessage) {
        try (BufferedWriter writer = Files.newBufferedWriter(reportPath,
                StandardCharsets.UTF_8, StandardOpenOption.APPEND)) {
            writer.write(String.format("%s,%s,%s,%s,\"%s\"%n",
                    stockNo, originalFilename, newFilename, status,
                    errorMessage.replace("\"", "\"\"")));
        } catch (IOException e) {
            System.err.println("写入报告失败: " + e.getMessage());
        }
    }

    /**
     * 初始化报告文件
     */
    private static void initReportFile(Path reportPath) throws IOException {
        if (!Files.exists(reportPath)) {
            try (BufferedWriter writer = Files.newBufferedWriter(reportPath, StandardCharsets.UTF_8)) {
                writer.write("stock_no,original_filename,new_filename,status,error_message");
                writer.newLine();
            }
            System.out.println("已初始化报告文件: " + reportPath);
        }
    }

    /**
     * 初始化错误日志文件
     */
    private static void initErrorLogFile(Path errorLogPath) throws IOException {
        if (!Files.exists(errorLogPath)) {
            Files.createFile(errorLogPath);
            System.out.println("已创建错误日志文件: " + errorLogPath);
        }
    }

    /**
     * 加载已处理的stock_no
     */
    private static Set<String> loadProcessedStockNos(Path reportPath) throws IOException {
        Set<String> processed = new HashSet<>();
        if (!Files.exists(reportPath)) {
            return processed;
        }

        try (BufferedReader reader = Files.newBufferedReader(reportPath, StandardCharsets.UTF_8)) {
            // 跳过标题行
            String line = reader.readLine();
            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(",", 5);
                if (parts.length >= 1) {
                    processed.add(parts[0].trim());
                }
            }
        }
        return processed;
    }

    /**
     * 从XLSX文件加载数据
     */
    private static List<ImageRecord> loadXlsxData(Path xlsxPath) throws IOException {
        List<ImageRecord> records = new ArrayList<>();

        try (InputStream is = Files.newInputStream(xlsxPath);
             Workbook workbook = new XSSFWorkbook(is)) {

            Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
            Iterator<Row> rowIterator = sheet.iterator();

            // 跳过标题行
            if (rowIterator.hasNext()) {
                rowIterator.next();
            }

            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                Cell stockNoCell = row.getCell(0);
                Cell urlCell = row.getCell(1);

                if (stockNoCell == null || urlCell == null) {
                    System.err.println("跳过空行: " + row.getRowNum());
                    continue;
                }

                // 创建记录对象
                ImageRecord record = new ImageRecord();
                record.stockNo = getCellValueAsString(stockNoCell).trim();
                record.downloadUrl = getCellValueAsString(urlCell).trim();

                records.add(record);
            }
        }
        return records;
    }

    /**
     * 获取单元格的值作为字符串
     */
    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    // 防止科学计数法
                    double num = cell.getNumericCellValue();
                    if (num == (long) num) {
                        return String.valueOf((long) num);
                    } else {
                        return String.valueOf(num);
                    }
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            case BLANK:
                return "";
            default:
                return "";
        }
    }

    /**
     * 过滤出未处理的记录
     */
    private static List<ImageRecord> filterPendingRecords(List<ImageRecord> records, Set<String> processedStockNos) {
        List<ImageRecord> pending = new ArrayList<>();
        for (ImageRecord record : records) {
            if (!processedStockNos.contains(record.stockNo)) {
                pending.add(record);
            }
        }
        return pending;
    }

    /**
     * 从URL中提取原始文件名
     */
    private static String extractFilenameFromUrl(String url) {
        try {
            // 移除URL参数
            int paramIndex = url.indexOf('?');
            String cleanUrl = (paramIndex > 0) ? url.substring(0, paramIndex) : url;

            // 从路径中获取文件名
            int lastSlash = cleanUrl.lastIndexOf('/');
            if (lastSlash != -1 && lastSlash < cleanUrl.length() - 1) {
                return cleanUrl.substring(lastSlash + 1);
            }
        } catch (Exception e) {
            System.err.println("无法从URL提取文件名: " + url);
        }
        // 如果无法提取，使用默认文件名
        return "unknown.jpg";
    }

    /**
     * 创建目录（如果不存在）
     */
    private static void createDirIfNotExists(Path dir) throws IOException {
        if (!Files.exists(dir)) {
            Files.createDirectories(dir);
            System.out.println("创建目录: " + dir);
        }
    }

//    /**
//     * 加载最后文件编号
//     */
//    private static int loadLastFileNumber(Path reportPath) throws IOException {
//        if (!Files.exists(reportPath)) return 0;
//
//        try (BufferedReader reader = Files.newBufferedReader(reportPath)) {
//            return reader.lines()
//                    .skip(1) // 跳过标题行
//                    .map(line -> line.split(","))
//                    .filter(parts -> parts.length > 2)
//                    .map(parts -> parts[2]) // new_filename列
//                    .filter(name -> name.matches(".*原图4_\\d+\\.jpg"))
//                    .mapToInt(name -> {
//                        String num = name.replaceAll(".*原图4_(\\d{1,5})\\.jpg", "$1");
//                        return Integer.parseInt(num);
//                    })
//                    .max()
//                    .orElse(0);
//        }
//    }
    /**
     * 加载最后文件编号（修复版）
     */
    private static int loadLastFileNumber(Path reportPath) throws IOException {
        if (!Files.exists(reportPath)) return 0;

        try (BufferedReader reader = Files.newBufferedReader(reportPath)) {
            return reader.lines()
                    .skip(1) // 跳过标题行
                    .map(line -> line.split(","))
                    .filter(parts -> parts.length > 2)
                    .map(parts -> parts[2].trim()) // new_filename列
                    .filter(name -> name.matches(".*(原图4_|/原图4_)\\d+\\.jpg")) // 兼容两种格式
                    .mapToInt(name -> {
                        // 处理两种可能的格式：
                        // 1. "原图4_0001234.jpg"
                        // 2. "batch_1/原图4_0001234.jpg"
                        String numStr = name.replaceAll(".*[^0-9](\\d+)\\.jpg$", "$1");
                        try {
                            return Integer.parseInt(numStr);
                        } catch (NumberFormatException e) {
                            System.err.println("无法解析文件编号: " + name);
                            return 0;
                        }
                    })
                    .max()
                    .orElse(0);
        }
    }
    /**
     * 图片记录类
     */
    static class ImageRecord {
        String stockNo;
        String downloadUrl;
    }
}